package org.tamal.struts.struts;

import java.util.Map;

import org.apache.log4j.Logger;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.interceptor.validation.SkipValidation;
import org.tamal.HttpSession;
import org.tamal.Util;
import org.tamal.annotation.AllowHttpGet;
import org.tamal.annotation.SkipAuthentication;
import org.tamal.persistence.User;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.validator.annotations.CustomValidator;
import com.opensymphony.xwork2.validator.annotations.EmailValidator;
import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
import com.opensymphony.xwork2.validator.annotations.StringLengthFieldValidator;

/**
 * Login Action validates a user to log in to the web site.
 * After validation, the action save the credential in session.
 *
 * @author Tamal Kanti Nath
 */
@Results({
    @Result(name = Action.SUCCESS, location = "/", type = "redirect"),
    @Result(name = Action.INPUT, location = "login.jsp") })
public final class LoginAction extends ActionSupport implements SessionAware {

    private static final long serialVersionUID = 721572728671086623L;
    private final transient Logger log = Logger.getLogger(getClass().getName());
    private Map<String, Object> session;
    private String email;
    private String password;
    private String submit;

    /**
     * This method validates and sets the login credential.
     * This method is only called when 'login' action gets called with method
     * specified as 'login' i.e. the request having 'method:login' as parameter
     * key. Basic validation is done by validation interceptor before the method
     * gets called.
     * The method returns SUCCESS if the session already has an user. Otherwise
     * it validates the login credential and sets the email in the session.
     *
     * @return SUCCESS if credential match or already logged in;
     *  INPUT otherwise.
     */
    @Override
    @SkipAuthentication
    public String execute() {
        User user = (User) session.get(HttpSession.USER);
        if (user == null) {
            user = new User();
            user.setEmail(email);
            user.setPassword(password);
            String msg = Util.getService().login(user);
            if (msg == null) {
                session.put(HttpSession.USER.name(), user);
                log.info(user.getName() + " logged in");
            } else {
                msg = getText(msg);
                addActionError(msg);
                log.info(email + ": " + msg);
                return INPUT;
            }
        } else {
            log.info(user.getName() + " already logged in");
        }
        return SUCCESS;
    }

    /**
     * This action clears the user from session and returns LOGIN.
     * This method is called in two ways:
     * 1. As a request parameter 'method:logout' of 'login' action
     * 2. As a 'logout' action with no parameter
     *
     * @return LOGIN
     */
    @AllowHttpGet
    @SkipValidation
    @org.apache.struts2.convention.annotation.Action("logout")
    public String logout() {
        User user = (User) session.get(HttpSession.USER);
        if (user != null) {
            session.remove(HttpSession.USER);
            log.info(user.getName() + " is logged out");
        }
        return LOGIN;
    }

    /**
     * Sets the Map of session attributes in the implementing class.
     * @param session a Map of HTTP session attribute name/value pairs.
     */
    @Override
    public void setSession(Map<String, Object> session) {
        this.session = session;
    }

    /**
     * Returns the email.
     * @return the email
     */
    @RequiredStringValidator(key = "login.email.required", trim = true,
        shortCircuit = true)
    @EmailValidator(key = "login.email.invalid")
    public String getEmail() {
        return email;
    }

    /**
     * Sets the email.
     * @param email the email to set
     */
    public void setEmail(String email) {
        this.email = email;
    }

    /**
     * Returns the password.
     * @return the password
     */
    @RequiredStringValidator(key = "login.password.required", trim = false,
        shortCircuit = true)
    @StringLengthFieldValidator(key = "login.password.length", minLength = "8",
        maxLength = "16", shortCircuit = true)
    @CustomValidator(key = "login.password.strong", type = "strongPassword")
    public String getPassword() {
        return password;
    }

    /**
     * Sets the password.
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * Returns the submit.
     * @return the submit
     */
    public String getSubmit() {
        return submit;
    }

    /**
     * Sets the submit.
     * @param submit the submit to set
     */
    public void setSubmit(String submit) {
        this.submit = submit;
    }

}
